Fix panic=abort when compiling with plugins
authorAlex Crichton <alex@alexcrichton.com>
Thu, 4 Aug 2016 03:36:28 +0000 (20:36 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Thu, 4 Aug 2016 03:36:28 +0000 (20:36 -0700)
Closes #2738

src/cargo/ops/cargo_rustc/context.rs
src/cargo/ops/cargo_rustc/mod.rs
tests/plugins.rs

index fb9acab305592a4b3656fa21e959c8f4f7243903..49236afea9d813d3229b6904bf3c96b7b52f0a2b 100644 (file)
@@ -39,6 +39,7 @@ pub struct Context<'a, 'cfg: 'a> {
     pub build_config: BuildConfig,
     pub build_scripts: HashMap<Unit<'a>, Arc<BuildScripts>>,
     pub links: Links<'a>,
+    pub used_in_plugin: HashSet<Unit<'a>>,
 
     host: Layout,
     target: Option<Layout>,
@@ -91,6 +92,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
             build_scripts: HashMap::new(),
             build_explicit_deps: HashMap::new(),
             links: Links::new(),
+            used_in_plugin: HashSet::new(),
         })
     }
 
@@ -235,6 +237,41 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
         Ok(())
     }
 
+    /// Builds up the `used_in_plugin` internal to this context from the list of
+    /// top-level units.
+    ///
+    /// This will recursively walk `units` and all of their dependencies to
+    /// determine which crate are going to be used in plugins or not.
+    pub fn build_used_in_plugin_map(&mut self, units: &[Unit<'a>])
+                                    -> CargoResult<()> {
+        let mut visited = HashSet::new();
+        for unit in units {
+            try!(self.walk_used_in_plugin_map(unit,
+                                              unit.target.for_host(),
+                                              &mut visited));
+        }
+        Ok(())
+    }
+
+    fn walk_used_in_plugin_map(&mut self,
+                               unit: &Unit<'a>,
+                               is_plugin: bool,
+                               visited: &mut HashSet<(Unit<'a>, bool)>)
+                               -> CargoResult<()> {
+        if !visited.insert((*unit, is_plugin)) {
+            return Ok(())
+        }
+        if is_plugin {
+            self.used_in_plugin.insert(*unit);
+        }
+        for unit in try!(self.dep_targets(unit)) {
+            try!(self.walk_used_in_plugin_map(&unit,
+                                              is_plugin || unit.target.for_host(),
+                                              visited));
+        }
+        Ok(())
+    }
+
     /// Returns the appropriate directory layout for either a plugin or not.
     pub fn layout(&self, unit: &Unit) -> LayoutProxy {
         let primary = unit.pkg.package_id() == self.resolve.root();
index a743315b2480f0a10a42fe18e8e7c27a65da1b85..27719613905c772953ad644a12ed2968b116d17f 100644 (file)
@@ -89,6 +89,7 @@ pub fn compile_targets<'a, 'cfg: 'a>(ws: &Workspace<'cfg>,
 
     try!(cx.prepare());
     try!(cx.probe_target_info(&units));
+    try!(cx.build_used_in_plugin_map(&units));
     try!(custom_build::build_map(&mut cx, &units));
 
     for unit in units.iter() {
@@ -512,8 +513,20 @@ fn build_base_args(cx: &Context,
         cmd.arg("-C").arg(&format!("opt-level={}", opt_level));
     }
 
+    // If a panic mode was configured *and* we're not ever going to be used in a
+    // plugin, then we can compile with that panic mode.
+    //
+    // If we're used in a plugin then we'll eventually be linked to libsyntax
+    // most likely which isn't compiled with a custom panic mode, so we'll just
+    // get an error if we actually compile with that. This fixes `panic=abort`
+    // crates which have plugin dependencies, but unfortunately means that
+    // dependencies shared between the main application and plugins must be
+    // compiled without `panic=abort`. This isn't so bad, though, as the main
+    // application will still be compiled with `panic=abort`.
     if let Some(panic) = panic.as_ref() {
-        cmd.arg("-C").arg(format!("panic={}", panic));
+        if !cx.used_in_plugin.contains(unit) {
+            cmd.arg("-C").arg(format!("panic={}", panic));
+        }
     }
 
     // Disable LTO for host builds as prefer_dynamic and it are mutually
index 2da8965da2b95421b77815dafd774d363a5b0a4d..cd762377ab946c3b3d2c42810c574230ee32f96c 100644 (file)
@@ -274,3 +274,93 @@ fn native_plugin_dependency_with_custom_ar_linker() {
 [ERROR] could not exec the linker [..]
 "));
 }
+
+#[test]
+fn panic_abort_plugins() {
+    if !is_nightly() {
+        return
+    }
+
+    let bar = project("bar")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "bar"
+            version = "0.0.1"
+            authors = []
+
+            [profile.dev]
+            panic = 'abort'
+
+            [dependencies]
+            foo = { path = "foo" }
+        "#)
+        .file("src/lib.rs", "")
+        .file("foo/Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [lib]
+            plugin = true
+        "#)
+        .file("foo/src/lib.rs", r#"
+            #![feature(rustc_private)]
+            extern crate syntax;
+        "#);
+
+    assert_that(bar.cargo_process("build"),
+                execs().with_status(0));
+}
+
+#[test]
+fn shared_panic_abort_plugins() {
+    if !is_nightly() {
+        return
+    }
+
+    let bar = project("top")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "top"
+            version = "0.0.1"
+            authors = []
+
+            [profile.dev]
+            panic = 'abort'
+
+            [dependencies]
+            foo = { path = "foo" }
+            bar = { path = "bar" }
+        "#)
+        .file("src/lib.rs", "
+            extern crate bar;
+        ")
+        .file("foo/Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [lib]
+            plugin = true
+
+            [dependencies]
+            bar = { path = "../bar" }
+        "#)
+        .file("foo/src/lib.rs", r#"
+            #![feature(rustc_private)]
+            extern crate syntax;
+            extern crate bar;
+        "#)
+        .file("bar/Cargo.toml", r#"
+            [package]
+            name = "bar"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("bar/src/lib.rs", "");
+
+    assert_that(bar.cargo_process("build"),
+                execs().with_status(0));
+}